#ifndef _BBN_H__
#define _BBN_H__

#include "SDL.h"
#include <string>

using namespace std;

/* Auto-detect if we're using the SDL2 API by the headers available. */
#if !SDL_VERSION_ATLEAST(2,0,0)
    #error "Old SDL headers are included.  You need to use SDL 2+ instead."
#endif

namespace BBN
{
    class Target;
    
    
    class Color
    {
        public:
        
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
        
        Color();
        Color(unsigned char r, unsigned char g, unsigned char b);
        Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
        
        Color& rgb(unsigned char r, unsigned char g, unsigned char b);
        Color& rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
        Color& rgb_hex(Uint32 hex);
        Color& rgba_hex(Uint32 hex);
        
        Color& setR(unsigned char r);
        Color newR(unsigned char r) const;
        
        Color& setG(unsigned char g);
        Color newG(unsigned char g) const;
        
        Color& setB(unsigned char b);
        Color newB(unsigned char b) const;
        
        Color& setA(unsigned char a);
        Color newA(unsigned char a) const;
        
        Color add_clamped(int r, int g, int b, int a = 0) const;
        
        Color rgb_to_hsv() const;
        Color hsv_to_rgb() const;
        
        Uint32 to_format(SDL_PixelFormat* format) const;
        SDL_Color to_SDL_Color() const;
        
        std::string to_string() const;
        
        bool operator==(const Color& color) const;
        bool operator!=(const Color& color) const;
    };

    
    
    class Image
    {
        friend class Target;
    public:
        Image(unsigned int width, unsigned int height);
        Image(const string& filename);
        ~Image();
        
        bool failed() const;
        
        unsigned int getWidth() const;
        unsigned int getHeight() const;
        
        Image copy() const;
    private:
        void* data;
        
        Image();
    };
    
    class Shape
    {
        friend class Target;
    protected:
        Shape()
        {}
        
        virtual void draw(const Target& target, const Color& color) const = 0;
        virtual void fill(const Target& target, const Color& color) const
        {
            draw(target, color);
        }
    };
    
    class Point : public Shape
    {
    public:
        float x, y;
        
        Point(float x, float y);
    private:
        
        virtual void draw(const Target& target, const Color& color) const;
    };
    
    class Line : public Shape
    {
    public:
        float x1, y1, x2, y2;
        
        Line(float x1, float y1, float x2, float y2);
    private:
        
        virtual void draw(const Target& target, const Color& color) const;
    };
    
    class Rectangle : public Shape
    {
    public:
        float x1, y1, x2, y2;
        
        Rectangle(float x1, float y1, float x2, float y2);
    private:
        
        virtual void draw(const Target& target, const Color& color) const;
        virtual void fill(const Target& target, const Color& color) const;
    };
    
    class RoundedRectangle : public Shape
    {
    public:
        float x1, y1, x2, y2, radius;
        
        RoundedRectangle(float x1, float y1, float x2, float y2, float radius);
    private:
        
        virtual void draw(const Target& target, const Color& color) const;
        virtual void fill(const Target& target, const Color& color) const;
    };
    
    class Circle : public Shape
    {
    public:
        float x, y, radius;
        
        Circle(float x, float y, float radius);
    private:
        
        virtual void draw(const Target& target, const Color& color) const;
        virtual void fill(const Target& target, const Color& color) const;
    };
    
    
    
    class Font
    {
        friend class Target;
    public:
        Font(const string& filename, unsigned int point_size);
        ~Font();
        
        bool failed() const;
        
        unsigned int getHeight() const;
        unsigned int getWidth(const string& text) const;
        unsigned int getHeight(const string& text) const;
        
    private:
        void* data;
        
        Font();
    };
    
    
    
    class Target
    {
        friend class Shape;
        friend class Point;
        friend class Line;
        friend class Rectangle;
        friend class RoundedRectangle;
        friend class Circle;
    public:
        
        Target(const Image& image);
        virtual ~Target();
        
        unsigned int getWidth() const;
        unsigned int getHeight() const;
        
        void clear();
        void clear(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255);
        
        void draw(const Image& image, float x, float y);
        void draw(const Image& image, float x, float y, float angle);
        void draw(const Image& image, float x, float y, float scale_x, float scale_y);
        void draw(const Image& image, float x, float y, float angle, float scale_x, float scale_y);
        
        void draw(const Shape& shape, const Color& color) const;
        void fill(const Shape& shape, const Color& color) const;
        
        void draw(const Font& font, const string& text, float x, float y, const Color& color = Color(0,0,0));
        
    protected:
        Target();
        
        void* data;
    };
    
    class Screen : public Target
    {
    public:
        
        Screen(unsigned int width, unsigned int height);
        virtual ~Screen();
        
        bool failed() const;
        
        void show() const;
        
    private:
        
        bool initialize(unsigned int width, unsigned int height);
        void shut_down();
        
    };
    
    
    class Input;
    
    
    class Gamepad
    {
    public:
        
        class Button
        {
        public:
            
            Gamepad& gamepad;
            unsigned int id;
            
            Button(Gamepad& gamepad, unsigned int id);
            
            bool pressed() const;
            bool released() const;
            bool held() const;
        };
        
        
        class Joystick
        {
        public:
            
            Gamepad& gamepad;
            unsigned int id;
            
            unsigned int horizontal_axis_id, vertical_axis_id;
            int last_horizontal_state, last_vertical_state;
            int event_horizontal_state, event_vertical_state;
            int dead_zone;
            
            Joystick(Gamepad& gamepad, unsigned int id, unsigned int horizontal_axis_id, unsigned int vertical_axis_id);
            
            bool pressed_up() const;
            bool released_up() const;
            float value_up() const;
            
            bool pressed_down() const;
            bool released_down() const;
            float value_down() const;
            
            bool pressed_left() const;
            bool released_left() const;
            float value_left() const;
            
            bool pressed_right() const;
            bool released_right() const;
            float value_right() const;
        };
        
        Input& input;
        unsigned int id;
        
        Joystick joystick1;
        Joystick joystick2;
        Joystick joystick3;
        
        Button button1;
        Button button2;
        Button button3;
        Button button4;
        
        Gamepad(Input& input, unsigned int id);
        
    };
    
    
    class Input
    {
        friend class Gamepad::Button;
        friend class Gamepad::Joystick;
    public:
        Input();
        virtual ~Input();
        
        void reload_gamepads();
        
        unsigned int get_num_gamepads() const;
        
        bool next_event();
        
        bool is_quit() const;
        bool is_key_event() const;
        bool is_mouse_event() const;
        bool is_gamepad_event() const;
        
        Gamepad& get_event_gamepad();
        
        bool key_pressed(SDL_Scancode key);
        bool key_released(SDL_Scancode key);
        bool key_held(SDL_Scancode key);
        
        bool mouse_pressed(int button);
        bool mouse_released(int button);
        bool mouse_held(int button);
        
        void update_mouse();
        int mouse_x() const;
        int mouse_y() const;
        
        struct GamepadSet
        {
            Input& input;
            
            GamepadSet(Input& input);
            Gamepad& operator[](int index);
            Gamepad const& operator[](int index) const;
        };
        
        GamepadSet gamepads;
        
        Gamepad gamepad1;
        Gamepad gamepad2;
        Gamepad gamepad3;
        Gamepad gamepad4;
        
        
    private:
        void* data;
        int _mx, _my;
    };
    
    
    
    float get_total_time();
    void print_line(const string& str);
    void wait(float seconds);
    
}

using namespace BBN;

#endif

